
#include <time.h>
#include <stdio.h>
#include <BOpenGL>
#include <BPainter>
#include <BKeyEvent>
#include <BCharEvent>
#include <BEvent>
#include <BMouseEvent>
#include <BFont>
#include <BRange>
#include <BStyle>
#include <BSystem>

#include <BLineEdit>
#include "member_BLineEdit.h"

using namespace BWE;

#define member					(*(member_BLineEdit*)_ptr)
#define member_allocate()		_ptr = new member_BLineEdit(this)
#define member_release()		delete (member_BLineEdit*)_ptr

BLineEdit::BLineEdit(BWidget* parent)
{
	member_allocate();
	this->setParent(parent);
	this->setMargin(3);
	this->setCursor(Cursor_Ibeam);
}
BLineEdit::BLineEdit(const BString& text, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	this->setMargin(3);
	this->setText(text);
	this->setCursor(Cursor_Ibeam);
}
BLineEdit::~BLineEdit()
{
	member_release();
}

void BLineEdit::setAlign(Align align)
{
	if(member.align != align)
	{
		member.align = align;
		member.dirty = true;
		this->fresh();
	}
}
Align BLineEdit::align() const
{
	return member.align;
}

void BLineEdit::setText(const BString& text)
{
	if (member.block.text != text)
	{
		if (text.contain('\n'))
		{
			BString temp = text;
			temp.remove('\n');
			member.block.reset(temp);
		}
		else
		{
			member.block.reset(text);
		}
		member.dirty = true;
		member.editing = false;
		emit(Signal_TextChanged, member.block.text);
		this->fresh();
	}
}
const BString& BLineEdit::text() const
{
	return member.block.text;
}

void BLineEdit::setReadOnly(bool readOnly)
{
	if (member.readOnly != readOnly)
	{
		member.readOnly = readOnly;
		member.dirty = true;
		if(member.readOnly)
			this->setCursor(Cursor_None);
		else
			this->setCursor(Cursor_Ibeam);
		this->fresh();
	}
}
bool BLineEdit::readOnly() const
{
	return member.readOnly;
}

bool BLineEdit::setFormat(BTextFormat* format)
{
	if (member.textFormat != format)
	{
		member.textFormat = format;
		member.block.dirtyCells = true;
		member.block.dirtyFont = true;
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
const BTextFormat* BLineEdit::format() const
{
	return member.textFormat;
}
BTextFormat* BLineEdit::format()
{
	return member.textFormat;
}

void BLineEdit::setPrefix(const BString& prefix)
{
	if (member.prefix != prefix)
	{
		member.prefix = prefix;
		member.dirtySize = true;
		member.dirty = true;
		this->fresh();
	}
}
const BString& BLineEdit::prefix() const
{
	return member.prefix;
}

void BLineEdit::setSuffix(const BString& suffix)
{
	if (member.suffix != suffix)
	{
		member.suffix = suffix;
		member.dirtySize = true;
		member.dirty = true;
		this->fresh();
	}
}
const BString& BLineEdit::suffix() const
{
	return member.suffix;
}

bool BLineEdit::query(const BString& text, BValue& value) const
{
	if (text == "text-size")
	{
		value = member.block.rect.size();
		return true;
	}
	if (text == "text-rect")
	{
		value = member.block.rect;
		return true;
	}
	return BWidget::query(text, value);
}

BSize BLineEdit::sizeHint() const
{
	BSize hsize = BWidget::sizeHint();
	if (const BFont* font = this->font())
	{
		int cw = font->codeWidth('0');
		hsize.width() += font->size() * 8;
		hsize.height() += font->size() + 3;
		if (member.dirtySize)
		{
			member.freshSizes();
			member.dirtySize = false;
		}
		if (member.prefixSize.valid())
		{
			hsize.width() += member.prefixSize.width();
			hsize.height() = bMax(hsize.height(), member.prefixSize.height());
		}
		if (member.suffixSize.valid())
		{
			hsize.width() += member.suffixSize.width();
			hsize.height() = bMax(hsize.height(), member.suffixSize.height());
		}
	}
	return hsize;
}

void BLineEdit::updateEvent(const BEvent& event)
{
	if (this->focused() && !member.readOnly)
	{
		bool inputShown = true;
		if (member.blinkTime > 0.0)
		{
			BReal now = event.now();
			BReal time = now - member.blinkTick;
			inputShown = time <= (member.blinkTime * 0.5);
			if (time > member.blinkTime)
				member.blinkTick = now;
		}
		if (member.inputShown != inputShown)
		{
			member.inputShown = inputShown;
			this->fresh();
		}
	}
}
void BLineEdit::resizeEvent(const BEvent& event)
{
	member.dirty = true;
	BWidget::resizeEvent(event);
}

void BLineEdit::styleEvent(const BEvent& event)
{
	BWidget::styleEvent(event);
	if (const BStyle* style = event.value())
	{
		const BValue& alignValue = style->value(Value_Align);
		if (alignValue.valid())
			member.align = alignValue;

		member.block.setFont(this->font());
		member.dirty = true;
	}
}
void BLineEdit::freshEvent(const BEvent& event)
{
	if (member.dirty)
	{
		member.freshRects();
		member.dirty = false;
	}
	if (member.dirtyLine)
	{
		member.freshInputLine();
		member.dirtyLine = false;
	}
	BWidget::freshEvent(event);
}
void BLineEdit::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();

	if (this->focused() && member.block.inputCount)
	{
		const BRect& crect = this->centerRect();
		const BPoint& pos = member.block.rect.pos();
		painter.setColor(Color_Background_Checked);
		int begin = bMin(member.block.inputIndex, member.block.inputIndex + member.block.inputCount);
		int end = bMax(member.block.inputIndex, member.block.inputIndex + member.block.inputCount);
		for (int i = begin; i < end; i++)
		{
			const CodeCell& cell = member.block.cells[i];
			BRect cellRect = cell.rect + pos;
			painter.fillRect(cellRect);
		}
	}

	if (this->enabled())
	{
		if (member.prefix.size() && painter.setColor(Color_Prefix))
			painter.drawText(member.prefixRect, member.prefix);
		if (member.suffix.size() && painter.setColor(Color_Suffix))
			painter.drawText(member.suffixRect, member.suffix);
		if (painter.setColor(Color_Text))
			member.block.paint(painter);
	}
	else
	{
		if (member.prefix.size() && painter.setColor(Color_Prefix_Disable))
			painter.drawText(member.prefixRect, member.prefix);
		if (member.suffix.size() && painter.setColor(Color_Suffix_Disable))
			painter.drawText(member.suffixRect, member.suffix);
		if (painter.setColor(Color_Text_Disable))
			member.block.paint(painter);
	}

	painter.drawBorder();
	painter.drawPerch();
	painter.drawMargin();

	if (!member.readOnly && member.inputShown && member.block.inputCount == 0)
	{
		painter.setColor(Color_Indicator);;
		painter.drawLine(member.lineBegin, member.lineEnd);
	}
}

void BLineEdit::focusEvent(const BEvent& event)
{
	if (!member.readOnly)
	{
		if (this->focused())
		{
			member.focusedTime = event.time();
			member.block.inputIndex = 0;
			member.block.inputCount = member.block.cells.size();
		}
		else
		{
			if (member.editing)
			{
				member.inputShown = false;
				member.editing = false;
				emit(Signal_EditFinished, member.block.text);
			}
			member.inputShown = false;
		}
		member.dirty = true;
		member.dirtyLine = true;
		this->fresh();
	}
	BWidget::focusEvent(event);
}
void BLineEdit::keyDownEvent(const BKeyEvent& keyEvent)
{
	Key key = keyEvent.key();
	if (keyEvent.keyState(Key_LCtrl))
	{
		if (key == Key_V)
		{
			if (member.block.removeSelection())
			{
				member.editing = true;
				member.dirty = true;
			}
			BString text;
			bool ret = BSystem::GetClipBoardText(text);
			text.replace('\n', ' ');
			member.block.input(text);
			member.editing = true;
			member.dirty = true;
			this->emit(Signal_TextEdited, member.block.text);
			this->fresh();
		}
		else if (key == Key_C)
		{
			BString text;
			if (member.block.inputIndex >= 0)
			{
				if (member.block.inputCount)
					text = member.block.selection();
				else
					text = member.block.text;
			}
			BSystem::SetClipBoardText(text);
		}
		else if (key == Key_X)
		{
			BString text;
			if (member.block.inputIndex >= 0)
			{
				if (member.block.inputCount)
					text = member.block.selection();
				else
					text = member.block.text;
			}
			BSystem::SetClipBoardText(text);
			if (member.block.removeSelection())
			{
				member.dirtyLine = true;
				member.editing = true;
				member.dirty = true;
				this->emit(Signal_TextEdited, member.block.text);
				this->fresh();
			}
		}
	}
	else if (key == Key_Enter)
	{
		this->setFocused(false);
	}
	else if (key == Key_Home)
	{
		if (member.block.inputCount)
		{
			member.block.inputCount = 0;
			member.dirtyLine = true;
			this->fresh();
		}
		else if (member.block.inputIndex > 0)
		{
			member.block.inputIndex = 0;
			member.dirtyLine = true;
			this->fresh();
		}
		member.blinkTick = keyEvent.now() + 0.5;
	}
	else if (key == Key_End)
	{
		if (member.block.inputCount)
		{
			member.block.inputIndex += member.block.inputCount;
			member.block.inputCount = 0;
			member.dirtyLine = true;
			this->fresh();
		}
		else if (member.block.inputIndex < member.block.cells.size())
		{
			member.block.inputIndex = member.block.cells.size();
			member.dirtyLine = true;
			this->fresh();
		}
		member.blinkTick = keyEvent.now() + 0.5;
	}
	else if (key == Key_Left)
	{
		if (member.block.inputCount)
		{
			member.block.inputCount = 0;
			member.dirtyLine = true;
			this->fresh();
		}
		else if (member.block.inputIndex > 0)
		{
			member.block.inputIndex--;
			member.dirtyLine = true;
			this->fresh();
		}
		member.blinkTick = keyEvent.now() + 0.5;
	}
	else if (key == Key_Right)
	{
		if (member.block.inputCount)
		{
			member.block.inputIndex += member.block.inputCount;
			member.block.inputCount = 0;
			member.dirtyLine = true;
			this->fresh();
		}
		else if (member.block.inputIndex < member.block.cells.size())
		{
			member.block.inputIndex++;
			member.dirtyLine = true;
			this->fresh();
		}
		member.blinkTick = keyEvent.now() + 0.5;
	}
	else if (key == Key_Back)
	{
		if (member.block.inputCount)
		{
			if (member.block.removeSelection())
			{
				member.block.inputCount = 0;
				member.editing = true;
				member.dirty = true;
				emit(Signal_TextEdited, member.block.text);
				member.dirtyLine = true;
				this->fresh();
			}
		}
		else if (member.block.inputIndex > 0)
		{
			member.block.inputIndex--;
			if (member.block.remove(member.block.inputIndex))
			{
				member.editing = true;
				member.dirty = true;
				emit(Signal_TextEdited, member.block.text);
				member.dirtyLine = true;
				this->fresh();
			}
		}
		member.blinkTick = keyEvent.now() + 0.5;
	}
	else if (key == Key_Delete)
	{
		if (member.block.inputCount)
		{
			if (member.block.removeSelection())
			{
				member.editing = true;
				member.dirty = true;
				emit(Signal_TextEdited, member.block.text);
				member.dirtyLine = true;
				this->fresh();
			}
		}
		else
		{
			if (member.block.remove(member.block.inputIndex))
			{
				member.editing = true;
				member.dirty = true;
				emit(Signal_TextEdited, member.block.text);
				member.dirtyLine = true;
				this->fresh();
			}
		}
		member.blinkTick = keyEvent.now() + 0.5;
	}
	BWidget::keyDownEvent(keyEvent);
}
void BLineEdit::keyUpEvent(const BKeyEvent& keyEvent)
{
	member.pressed = false;
	member.pressedIndex = -1;
	this->fresh();
	BWidget::keyUpEvent(keyEvent);
}
void BLineEdit::charEvent(const BCharEvent& charEvent)
{
	BCode code = charEvent.code();
	bool edited = member.block.removeSelection();
	if (member.block.input(code))
	{
		edited = true;
		member.blinkTick = charEvent.now() + 0.5;
	}
	if (edited)
	{
		member.editing = true;
		member.dirty = true;
		emit(Signal_TextEdited, member.block.text);
		member.dirtyLine = true;
		this->fresh();
	}
	BWidget::charEvent(charEvent);
}

void BLineEdit::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	member.hoverIndex = 0;
	BWidget::mouseEnterEvent(mouseEvent);
}
void BLineEdit::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	member.hoverIndex = 0;
	BWidget::mouseLeaveEvent(mouseEvent);
}

void BLineEdit::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.time() - member.focusedTime < (BReal)0.3)
	{
		return;
	}
	if (mouseEvent.button() == Button_Left)
	{
		int hoverIndex = member.hoverIndex;
		if (member.block.inputCount || member.block.inputIndex != member.hoverIndex)
		{
			if (BSystem::KeyState(Key_LShift))
			{
				member.block.inputCount = member.hoverIndex - member.block.inputIndex;
			}
			else
			{
				member.block.inputIndex = member.hoverIndex;
				member.block.inputCount = 0;
			}
			member.dirtyLine = true;
			this->fresh();
		}
		member.pressed = true;
		member.pressedIndex = member.hoverIndex;
		member.blinkTick = mouseEvent.now() + 0.5;
	}
	BWidget::mousePressEvent(mouseEvent);
}
void BLineEdit::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	member.pressed = false;
	this->fresh();
	BWidget::mouseReleaseEvent(mouseEvent);
}
void BLineEdit::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	BPoint pos = mouseEvent.pos();
	pos.y() = this->height() / 2;
	int index = member.block.indexAt(pos);
	if (member.hoverIndex != index)
	{
		member.hoverIndex = index;
	}
	if (member.pressed)
	{
		int inputCount = member.hoverIndex - member.pressedIndex;
		if (member.block.inputIndex != member.hoverIndex || member.block.inputCount != inputCount)
		{
			member.block.inputCount = inputCount;
			member.dirtyLine = true;
			this->fresh();
		}
	}
	BWidget::mouseMoveEvent(mouseEvent);
}
void BLineEdit::mouseDoubleClickEvent(const BMouseEvent& mouseEvent)
{
	member.block.selectWord();
	member.dirtyLine = true;
	this->fresh();
	BWidget::mouseDoubleClickEvent(mouseEvent);
}

